package user

import (
	"context"
	"errors"
	"fmt"
	"gitcode.com/G-YT/iHealth25/server/global"
	"gitcode.com/G-YT/iHealth25/server/model/common/response"
	"gitcode.com/G-YT/iHealth25/server/model/system"
	"gitcode.com/G-YT/iHealth25/server/model/system/request"
	"gitcode.com/G-YT/iHealth25/server/model/user"
	userReq "gitcode.com/G-YT/iHealth25/server/model/user/request"
	userRes "gitcode.com/G-YT/iHealth25/server/model/user/response"
	"gitcode.com/G-YT/iHealth25/server/utils"
	"github.com/gin-gonic/gin"
	"github.com/gofrs/uuid/v5"
	jwt "github.com/golang-jwt/jwt/v4"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"strconv"
	"time"
)

type BaseApi struct {
}

// SignUp 注册
// @Tags User
// @Summary 注册
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body user.SignUp true "注册"
// @Success 200 {string} string "{"code":0,"data":{},"msg":"注册成功"}"
// @Router /api/user/signUp [post]
func (b *BaseApi) SignUp(c *gin.Context) {
	var r userReq.Register
	err := c.ShouldBindJSON(&r)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	err = utils.Verify(r, utils.UserSignUpVerify)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	// 判断手机号是否已存在
	phone, _ := UserService.FindUserByWhere(map[string]interface{}{"phone": r.Phone}, "id")
	if phone.ID != 0 {
		response.Result(7, "", "该号码已注册,请登录!", c)
		return
	}
	uuID := uuid.Must(uuid.NewV4())
	userData := &user.User{
		GVA_MODEL: global.GVA_MODEL{},
		UUID:      uuID,
		Phone:     r.Phone,
		Password:  r.Password,
		Name:      "fdNick_" + r.Phone[len(r.Phone)-4:],
	}
	// 判断邀请人是否存在
	if r.StewardID != 0 { // 如果邀请人是健管师
		steward, err := HealthManagerService.GetHealthManagerInfo(r.StewardID)
		if err != nil {
			response.Result(7, "", "该健管师不存在,请核实!", c)
			return
		}
		userData.StewardID = steward.HmId
		userData.ServiceCenterID = steward.StoreId
		userData.InviteUserID = 0
	}
	if r.InviteUserID != 0 { //如果邀请人是用户
		inviteUser, err := UserService.FindUserByID(r.InviteUserID)
		if err != nil {
			response.Result(7, "", "邀请用户不存在,请核实!", c)
			return
		}
		userData.StewardID = inviteUser.StewardID
		userData.ServiceCenterID = inviteUser.ServiceCenterID
		userData.InviteUserID = inviteUser.ID
		// 添加完成方案积分相关
		err = UserService.AddPoints(r.InviteUserID, 10, 6)
	}
	// 判断短信验证码
	//key := "Sms:Phone:Code:" + r.Phone
	//captcha, _ := global.GVA_REDIS.Get(context.Background(), key).Result()
	//if captcha != r.Code {
	//	response.Result(7, "", "验证码有误,请核实!", c)
	//	return
	//}
	var userID uint
	userID, err = UserService.Register(userData)
	data := map[string]interface{}{"id": userID, "phone": r.Phone}
	if err != nil {
		response.FailWithDetailed("", err.Error(), c)
		return
	}
	response.Result(200, data, "注册成功", c)
}

// SignIn 登陆
func (b *BaseApi) SignIn(c *gin.Context) {
	var l userReq.Login
	err := c.ShouldBindJSON(&l)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	err = utils.Verify(l, utils.UserSignInVerify)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	u := &user.User{Phone: l.Phone, Password: l.Password}
	userInter, err := UserService.Login(u)
	if err != nil {
		global.GVA_LOG.Error("登陆失败! 账号不存在或者密码错误!", zap.Error(err))
		response.FailWithMessage("用户名不存在或者密码错误", c)
		return
	}
	b.TokenNextC(c, *userInter)
	return
}

// ParseToken 解析token
func (b *BaseApi) ParseToken(c *gin.Context) {
	token := utils.GetToken(c)
	if token == "" {
		response.NoAuth("未登录或非法访问", c)
		c.Abort()
		return
	}
	if isBlacklist(token) {
		response.FailWithDetailed(2, "您的帐户异地登陆或令牌失效", c)
		utils.ClearToken(c)
		c.Abort()
		return
	}
	j := utils.NewJWT()
	_, err := jwt.ParseWithClaims(token, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) {
		return j.SigningKey, nil
	})

	// parseToken 解析token包含的信息
	if err != nil {
		var ve *jwt.ValidationError
		if errors.As(err, &ve) {
			var ve *jwt.ValidationError
			if errors.As(err, &ve) {
				if ve.Errors&jwt.ValidationErrorMalformed != 0 {
					response.FailWithDetailed(2, "非token信息", c)
				} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
					response.FailWithDetailed(2, "token已失效", c)
				} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
					response.FailWithDetailed(2, "token未激活", c)
				} else {
					response.FailWithDetailed(2, "无法处理此token", c)
				}
			}
		}
	} else {
		response.Result(200, 1, "token未失效", c)
	}
}

// Authorize 授权
func (b *BaseApi) Authorize(c *gin.Context) {
	var r userReq.Authorize
	err := c.ShouldBindJSON(&r)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	// 判断微信OpenID是否已存在
	userInfo, _ := UserService.FindUserByWhere(map[string]interface{}{"open_id": r.OpenID}, "id")
	if userInfo.ID != 0 {
		fmt.Println(userInfo.ID)
		b.TokenNextC(c, *userInfo)
		return
	} else {
		uuID := uuid.Must(uuid.NewV4())
		userData := &user.User{
			GVA_MODEL: global.GVA_MODEL{},
			UUID:      uuID,
			OpenID:    r.OpenID,
		}
		authorizeErr := UserService.Authorize(userData)
		if authorizeErr != nil {
			response.FailWithDetailed("授权失败", err.Error(), c)
			return
		}
		b.TokenNextC(c, *userData)
		return
	}
}

// GetAppLink 获取APP下载链接
func (b *BaseApi) GetAppLink(c *gin.Context) {
	config, err := appConfigService.GetRedisByField([]string{"app_url"})
	if err != nil {
		response.FailWithMessage("获取失败", c)
		return
	}
	response.OkWithDetailed(config["app_url"], "获取成功", c)
}

// GetConfigByWhere 根据条件获取配置项
func (b *BaseApi) GetConfigByWhere(c *gin.Context) {
	key := c.Query("config")
	config, err := appConfigService.GetRedisByField([]string{key})
	if err != nil {
		response.FailWithMessage("获取失败", c)
		return
	}
	response.OkWithDetailed(config[key], "获取成功", c)
}

// GetBAppLink 获取APP下载链接
func (b *BaseApi) GetBAppLink(c *gin.Context) {
	config, err := appConfigService.GetRedisByField([]string{"jgs_url"})
	if err != nil {
		response.FailWithMessage("获取失败", c)
		return
	}
	response.OkWithDetailed(config["jgs_url"], "获取成功", c)
}

// TokenNextC C端登录以后签发jwt
func (b *BaseApi) TokenNextC(c *gin.Context, user user.User) {
	token, claims, err := utils.LoginTokenC(&user)
	if err != nil {
		global.GVA_LOG.Error("获取token失败!", zap.Error(err))
		response.FailWithMessage("获取token失败", c)
		return
	}
	if !global.GVA_CONFIG.System.UseMultipoint {
		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
		response.OkWithDetailed(userRes.LoginResponse{
			ID:              user.ID,
			ServiceCenterID: user.ServiceCenterID,
			StewardID:       user.StewardID,
			InviteUserID:    user.InviteUserID,
			Name:            user.Name,
			Avatar:          user.Avatar,
			Types:           user.Types,
			Token:           token,
			ExpiresAt:       claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
		}, "登录成功", c)
		return
	}

	if jwtStr, err := jwtService.GetRedisJWT("C:SignInToken:" + strconv.FormatUint(uint64(user.ID), 10)); err == redis.Nil {
		if err := utils.SetRedisJWT(token, "C:SignInToken:"+strconv.FormatUint(uint64(user.ID), 10)); err != nil {
			global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
			response.FailWithMessage("设置登录状态失败", c)
			return
		}
		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
		response.OkWithDetailed(userRes.LoginResponse{
			ID:              user.ID,
			ServiceCenterID: user.ServiceCenterID,
			StewardID:       user.StewardID,
			InviteUserID:    user.InviteUserID,
			Name:            user.Name,
			Avatar:          user.Avatar,
			Types:           user.Types,
			Token:           token,
			ExpiresAt:       claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
		}, "登录成功", c)
	} else if err != nil {
		global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
		response.FailWithMessage("设置登录状态失败", c)
	} else {
		var blackJWT system.JwtBlacklist
		blackJWT.Jwt = jwtStr
		if err := jwtService.JsonInBlacklist(blackJWT); err != nil {
			response.FailWithMessage("jwt作废失败", c)
			return
		}
		if err := utils.SetRedisJWT(token, "C:SignInToken:"+strconv.FormatUint(uint64(user.ID), 10)); err != nil {
			response.FailWithMessage("设置登录状态失败", c)
			return
		}
		utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
		response.OkWithDetailed(userRes.LoginResponse{
			ID:              user.ID,
			ServiceCenterID: user.ServiceCenterID,
			StewardID:       user.StewardID,
			InviteUserID:    user.InviteUserID,
			Name:            user.Name,
			Avatar:          user.Avatar,
			Types:           user.Types,
			Token:           token,
			ExpiresAt:       claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
		}, "登录成功", c)
	}
}

// ForgetPassword 忘记密码
// @Tags User
// @Summary 用户修改密码
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query user.User true "用id查询用户"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /api/base/forgetPassword [post]
func (b *BaseApi) ForgetPassword(c *gin.Context) {
	var uf userReq.ForgetPassword
	err := c.ShouldBindJSON(&uf)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	//userInfo := &userReq.ForgetPassword{Phone: uf.Phone}
	//user, err := UserService.CodeLogin(userInfo)
	user, _ := UserService.FindUserByWhere(map[string]interface{}{"phone": uf.Phone}, "*")
	if user.ID == 0 {
		response.FailWithMessage("该账号不存在,请注册!", c)
		return
	}
	key := "Sms:Phone:Code:" + uf.Phone
	captcha, _ := global.GVA_REDIS.Get(context.Background(), key).Result()
	if captcha != uf.Code {
		response.FailWithMessage("请输入正确验证码!", c)
		return
	}
	b.TokenNextC(c, *user)
	return
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: IsBlacklist
//@description: 判断JWT是否在黑名单内部
//@param: jwt string
//@return: bool

func isBlacklist(jwt string) bool {
	_, ok := global.BlackCache.Get(jwt)
	return ok
}
